package algorithms;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map.Entry;

import algorithms.models.FeatureBag;

import puppy.graph.AgeAggregator;
import puppy.graph.CreateGraphDirected;
import puppy.graph.CreateLM;
import puppy.graph.DeliciousEdge;
import puppy.graph.DeliciousEdgeTransformer;
import puppy.graph.DeliciousNode;
import puppy.graph.DeliciousNodeTransformer;
import puppy.graph.GraphExtractor;
import util.hashing.Sorting;
import util.ranker.RankingService;
import edu.uci.ics.jung.algorithms.scoring.PageRank;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.UndirectedSparseGraph;

public class FeatureExtractor implements RankingService {

	public CreateGraphDirected graph = null;
	CreateLM lm = null;
	double query_total = 0;

	DeliciousEdgeTransformer edge_transformer = null;
	DeliciousNodeTransformer node_transformer = null;
	AgeAggregator agg = null;
	public double epsilon = 0.000001;

	public double alpha = 0.2;
	public double gamma = 0.0;
	public int level = 2;

	public int max_iterations = 5;

	public double n_gram_weight = 2;
	public int mimimum_length_ngram = 2;

	private int results_printed = 1000;

	private Float query_threshold_freq = 3f;

	private int diversity_iterations = 7;
	private int diversity_top = 1;
	private int number_of_results = 50;

	
	public Hashtable<String, FeatureBag> features= new Hashtable<String, FeatureBag>();
	
	
	
	public FeatureExtractor(String path, String co_occur, String w_p,
			String d_p, String z_p, AgeAggregator agg, double alpha,
			int max_iterations) {

		this.agg = agg;
		this.alpha = alpha;
		this.max_iterations = max_iterations;
		graph = new CreateGraphDirected(agg, w_p, d_p, z_p);

		graph.initGraph(path);

		graph.ages.clear();
		graph.ages = null;
		// graph.createGraph(path);

		// initLM(co_occur);
	}

	public void initLM(String path) {
		if (path == null)
			return;

		lm = new CreateLM();
		lm.initLM(path);
	}

	public float calulateConditionalProb(Hashtable<String, Float> q) {

		float p = 0f;

		return p;

	}

	/*
	 * public Hashtable<String, Float> generatePossibleTokensNGram(String
	 * query_text) { Hashtable<String, Float> h = new Hashtable<String,
	 * Float>();
	 * 
	 * String ngram = query_text.replace(" ", "_"); String t[] =
	 * ngram.split("_");
	 * 
	 * if (graph.nodes.containsKey(ngram)) {
	 * 
	 * float prior =0; h.put(ngram, 0f);
	 * 
	 * }
	 * 
	 * HashSet<String> tokens = new HashSet<String>(); for (int i = 0; i <
	 * t.length; i++) {
	 * 
	 * 
	 * if (i + 2 < t.length) {
	 * 
	 * String m = t[i] + "_" + t[i + 1] + "_" + t[i + 2]; if
	 * (graph.nodes.containsKey(m) && !tokens.contains(t[i]) &&
	 * !tokens.contains(t[i+1]) &&!tokens.contains(t[i+2]) ) {
	 * 
	 * tokens.add(t[i]); tokens.add(t[i+1]); tokens.add(t[i+2]); h.put(m, 0f); }
	 * 
	 * }
	 * 
	 * 
	 * if (i + 1 < t.length) {
	 * 
	 * String m = t[i] + "_" + t[i + 1]; if (graph.nodes.containsKey(m) &&
	 * !tokens.contains(t[i]) && !tokens.contains(t[i+1])) {
	 * 
	 * tokens.add(t[i]); tokens.add(t[i+1]);
	 * 
	 * h.put(m, 0f); }
	 * 
	 * } if (graph.nodes.containsKey(t[i]) && !tokens.contains(t[i])) {
	 * 
	 * tokens.add(t[i]);
	 * 
	 * h.put(t[i], 0f); }
	 * 
	 * }
	 * 
	 * return h; }
	 */

	public ArrayList<Entry<String, Float>> performPageRankWithPriors(
			Hashtable<String, Float> q, String query, int limit) {
		// create list of query n_gram phrases
		// star_wars --> star_wars_lego
		// star_wars_lego --> star_wars, star_wars_games, etc
		// q=generatePossibleTokensNGram(query);

		DeliciousNodeTransformer transformer = new DeliciousNodeTransformer(q,
				agg, graph, lm, query, n_gram_weight, mimimum_length_ngram);

		edge_transformer = new DeliciousEdgeTransformer(agg);

		// RandomWalkWIthPriors<DeliciousNode, DeliciousEdge> ranker =
		// new RandomWalkWIthPriors<DeliciousNode,DeliciousEdge>
		// (graph.graph,edge_transformer,transformer, alpha);
		// ranker.initModifiers(transformer, edge_transformer);

		// System.exit(0);

		/*
		 * RandomWalkMix<DeliciousNode, DeliciousEdge> ranker = new
		 * RandomWalkMix<DeliciousNode,DeliciousEdge>
		 * (graph.graph,edge_transformer,transformer, alpha);
		 * ranker.initModifiers(transformer, edge_transformer);
		 * 
		 * ranker.setTolerance(epsilon);
		 * 
		 * ranker.acceptDisconnectedGraph(true);
		 * ranker.setMaxIterations(max_iterations);
		 * 
		 * //ranker.setEdgeWeights(edge_weights)
		 * //System.out.println("Running page rank.." + "\t"+ q);
		 * ranker.evaluate();
		 * 
		 * Enumeration<String> nodes = graph.nodes.keys(); Hashtable<String,
		 * Float> scores = new Hashtable<String, Float>(); while
		 * (nodes.hasMoreElements()) { String node = nodes.nextElement();
		 * 
		 * DeliciousNode no = graph.nodes.get(node);
		 * 
		 * if (no.getName().equals("") || !no.isTag() ||
		 * tagInQuery(no.getName(),query)) {
		 * 
		 * continue; }
		 * 
		 * 
		 * double s = ranker.getVertexScore(no); scores.put(no.getName(),
		 * (float) s);
		 * 
		 * 
		 * 
		 * }
		 * 
		 * Iterator<Entry<String, Float>> tuples =
		 * Sorting.getTopN_Tuples(scores, false, 150); HashSet<String> temp =
		 * new HashSet<String>();
		 * 
		 * int i=0; while (tuples.hasNext() ) { Entry<String, Float> t =
		 * tuples.next();
		 * 
		 * if(!wasSuggestionsSeenBefore(temp,t.getKey())){ i++; //
		 * results.add(t); if(i<results_printed) System.out.println(query+ "\t"+
		 * t.getKey() + "\t" + i +"\t" + t.getValue()); temp.add(t.getKey()); }
		 * }
		 * 
		 * 
		 * 
		 * //clean modifiers tuples=null; temp.clear(); temp=null;
		 * transformer.clean(); transformer=null;
		 * 
		 * 
		 * q.clear(); q=null;
		 * 
		 * return null; }
		 * 
		 * 
		 * 
		 * public ArrayList<Entry<String, Float>>
		 * performPageRanKAbsorving(Hashtable<String,Float>q, String query, int
		 * limit){
		 * 
		 * 
		 * 
		 * //create list of query n_gram phrases // star_wars --> star_wars_lego
		 * // star_wars_lego --> star_wars, star_wars_games, etc
		 * //q=generatePossibleTokensNGram(query);
		 * 
		 * 
		 * DeliciousNodeTransformer transformer = new DeliciousNodeTransformer(
		 * q,agg,graph,lm,query, n_gram_weight, mimimum_length_ngram);
		 * 
		 * 
		 * 
		 * edge_transformer = new DeliciousEdgeTransformer(agg);
		 * 
		 * 
		 * //RandomWalkWIthPriors<DeliciousNode, DeliciousEdge> ranker = // new
		 * RandomWalkWIthPriors<DeliciousNode,DeliciousEdge> //
		 * (graph.graph,edge_transformer,transformer, alpha); //
		 * ranker.initModifiers(transformer, edge_transformer);
		 * 
		 * 
		 * // System.exit(0);
		 * 
		 * /* RandomWalkAbsorving<DeliciousNode, DeliciousEdge> ranker = new
		 * RandomWalkAbsorving<DeliciousNode,DeliciousEdge>
		 * (graph.graph,edge_transformer,transformer, alpha);
		 * ranker.initModifiers(transformer, edge_transformer,q);
		 * 
		 * ranker.setTolerance(epsilon);
		 * 
		 * ranker.acceptDisconnectedGraph(true);
		 * ranker.setMaxIterations(max_iterations);
		 * 
		 * //ranker.setEdgeWeights(edge_weights)
		 * System.out.println("Running page rank.." + "\t"+ q);
		 * ranker.evaluate();
		 * 
		 * Enumeration<String> nodes = graph.nodes.keys(); Hashtable<String,
		 * Float> scores = new Hashtable<String, Float>(); while
		 * (nodes.hasMoreElements()) { String node = nodes.nextElement();
		 * 
		 * DeliciousNode no = graph.nodes.get(node);
		 * 
		 * if (no.getName().equals("") || !no.isTag() ||
		 * tagInQuery(no.getName(),query)) {
		 * 
		 * continue; }
		 * 
		 * 
		 * double s = ranker.getVertexScore(no); scores.put(no.getName(),
		 * (float) s);
		 * 
		 * 
		 * 
		 * }
		 * 
		 * Iterator<Entry<String, Float>> tuples =
		 * Sorting.getTopN_Tuples(scores, false, 150); HashSet<String> temp =
		 * new HashSet<String>();
		 * 
		 * int i=0; while (tuples.hasNext() ) { Entry<String, Float> t =
		 * tuples.next();
		 * 
		 * if(!wasSuggestionsSeenBefore(temp,t.getKey())){ i++; //
		 * results.add(t); if(i<results_printed) System.out.println(query+ "\t"+
		 * t.getKey() + "\t" + i +"\t" + t.getValue()); temp.add(t.getKey()); }
		 * }
		 * 
		 * 
		 * 
		 * //clean modifiers tuples=null; temp.clear(); temp=null;
		 * transformer.clean(); transformer=null;
		 * 
		 * 
		 * q.clear(); q=null;
		 */

		return null;
	}

	private boolean wasSuggestionsSeenBefore(HashSet<String> temp, String key) {
		// TODO Auto-generated method stub

		if (temp.contains(key + "s"))
			return true;

		if (temp.contains(key.substring(0, key.length() - 1)))
			return true;

		return false;
	}

	public Hashtable<String, Float> rerankQueryRepresentation(
			Hashtable<String, Float> q, int limit) {
		Hashtable<String, Float> r = new Hashtable<String, Float>();
		Iterator<String> temp = Sorting.getTopN(q, false, limit);
		while (temp.hasNext()) {
			String key = temp.next();

			r.put(key, q.get(key));
		}
		return r;

	}

	public Graph<DeliciousNode, DeliciousEdge> extractSubGraph(
			UndirectedSparseGraph<DeliciousNode, DeliciousEdge> original,
			int level, Hashtable<String, Float> q) {

		HashSet<DeliciousNode> set = getQueryNodes(q);

		if (q.size() == 0) {
			System.out.println("Query set was zero");
			System.exit(0);
		}

		System.out.println(set.size());

		GraphExtractor extractor = new GraphExtractor(level, set);
		// Graph<DeliciousNode, DeliciousEdge> sub =

		return extractor.extractGraph(original);

	}

	public HashSet<DeliciousNode> getQueryNodes(Hashtable<String, Float> q) {

		HashSet<DeliciousNode> set = new HashSet<DeliciousNode>();
		Enumeration<String> keys = q.keys();
		while (keys.hasMoreElements()) {

			String query = keys.nextElement();

			set.add(graph.nodes.get(query));
		}

		return set;

	}

	public ArrayList<Entry<String, Float>> performPageRank(
			Hashtable<String, Float> q, Hashtable<String, Float> q_topics,
			String query, int limit, HashSet<String> absorving_nodes) {

		/**
		 * Change this back
		 * 
		 * 
		 */
		// q=rerankQueryRepresentation(q, 10);

		/*
		 * extract subgraph
		 */

		UndirectedSparseGraph<DeliciousNode, DeliciousEdge> subgraph = new UndirectedSparseGraph<DeliciousNode, DeliciousEdge>();
		graph.subgraph = extractSubGraph(graph.graph1, level, q);
		// graph.subgraph=graph.graph1;

		// graph.subgraph=subgraph;
		System.out.println(graph.graph1.getVertexCount() + "\t  subgraph size:"
				+ graph.subgraph.getVertexCount());

		DeliciousNodeTransformer transformer = new DeliciousNodeTransformer(q,
				q_topics, agg, graph, lm, query, n_gram_weight,
				mimimum_length_ngram, absorving_nodes);

		DeliciousEdgeTransformer edge_transformer = new DeliciousEdgeTransformer(
				agg);

		// RandomWalkWIthPriors<DeliciousNode, DeliciousEdge> ranker = new
		// RandomWalkWIthPriors<DeliciousNode,DeliciousEdge>(graph.subgraph,transformer,
		// alpha);

		RandomWalkBackward<DeliciousNode, DeliciousEdge> ranker = new RandomWalkBackward<DeliciousNode, DeliciousEdge>(
				graph.subgraph, transformer, alpha, gamma);

		ranker.initModifiers(transformer, edge_transformer);

		ranker.setTolerance(epsilon);

		ranker.acceptDisconnectedGraph(true);
		ranker.setMaxIterations(max_iterations);

		// ranker.setEdgeWeights(edge_weights)
		// System.out.println("Running page rank..");
		ranker.evaluate();

		Iterator<DeliciousNode> nodes_subgraph = graph.subgraph.getVertices()
				.iterator();

		HashSet<String> borreme = new HashSet<String>();
		Hashtable<String, Float> scores = new Hashtable<String, Float>();

		Iterator<DeliciousNode> temp = subgraph.getVertices().iterator();
		Hashtable<String, ArrayList<Float>> temp1 = new Hashtable<String, ArrayList<Float>>();
		/*
		 * while(temp.hasNext()){ DeliciousNode t = temp.next();
		 * 
		 * if(temp1.containsKey(t.getName())){
		 * temp1.get(t.getName()).add(ranker.getVertexScore(t).floatValue());
		 * 
		 * }else{
		 * 
		 * ArrayList<Float> temp2 = new ArrayList<Float>();
		 * temp2.add(ranker.getVertexScore(t).floatValue());
		 * temp1.put(t.getName(), temp2); }
		 * 
		 * }
		 * 
		 * 
		 * Enumeration<String> keys = temp1.keys();
		 * 
		 * while(keys.hasMoreElements()){
		 * 
		 * String key = keys.nextElement(); System.out.print(key + " scores: "
		 * ); ArrayList<Float> a = temp1.get(key); for(int i =0; i <a.size();
		 * i++){
		 * 
		 * System.out.print(a.get(i)+"\t"); }
		 * 
		 * System.out.println("");
		 * 
		 * }
		 */

		while (nodes_subgraph.hasNext()) {

			DeliciousNode no = nodes_subgraph.next();
			if (no.getName().equals("") || !no.isTag()
					|| tagInQuery(no.getName(), query)
					|| no.getName().length() < 2) {

				continue;
			}

			double s = ranker.getVertexScore(no);
			scores.put(no.getName(), (float) s);

		}

		Iterator<Entry<String, Float>> tuples = Sorting.getTopN_Tuples(scores,
				false, number_of_results);
		ArrayList<Entry<String, Float>> results = new ArrayList<Entry<String, Float>>();
		int i = 0;
		while (tuples.hasNext()) {
			Entry<String, Float> t = tuples.next();
			i++;
			results.add(t);
			
			addFeatures(t,transformer,i);
			
			if (i < results_printed)
				System.out.println(query + "\t" + t.getKey() + "\t" + i + "\t"
						+ t.getValue());
		}

		tuples = null;
		return results;
	}

	private void addFeatures(Entry<String, Float> t, DeliciousNodeTransformer transformer, int rank) {
		// TODO Auto-generated method stub
		DeliciousNode node = transformer.graph.nodes.get(rank);
		String name = t.getKey();
		Float  score=t.getValue(); 
		
		FeatureBag f = new FeatureBag();
		if(features.containsKey(name)){
			 f = features.get(name);
			
		}
		
		
		f.score=Double.valueOf(score);
		f.rank=rank;
	
		f.q_lm= transformer.calculateQueryProb(node);
		
		f.kl=transformer.calculateKL(node);
		f.prob_kids= transformer.calculateForegroundProb(node);
		f.prob_adults= transformer.calculateBackgroundProb(node);
		
	}
	
	
	

	public void feedAbsorving(HashSet<String> absorving_nodes,
			ArrayList<Entry<String, Float>> first_iteration,
			ArrayList<Entry<String, Float>> results,
			HashSet<String> previous_results) {

		for (int i = 0; i < first_iteration.size() && i < diversity_top; i++) {
			if (!previous_results.contains(first_iteration.get(i).getKey())) {
				absorving_nodes.add(first_iteration.get(0).getKey());
				results.add(first_iteration.get(0));
				previous_results.add(first_iteration.get(0).getKey());
				first_iteration.remove(0);
			}

		}

	}

	public Hashtable<String, Float> reRankQueryTerms(
			Hashtable<String, Float> q, String query) {

		Hashtable<String, Float> re = new Hashtable<String, Float>();

		Enumeration<String> keys = q.keys();

		while (keys.hasMoreElements()) {

		}

		return re;

	}

	private boolean tagInQuery(String name, String query) {
		// TODO Auto-generated method stub

		// example tag star query star_wars

		query = query.replaceAll("_", " ");

		name = name.replaceAll("_", " ");

		String[] query_tokens = query.split(" ");
		String[] tag_tokens = name.split(" ");

		if (name.contains(query) && name.length() > query.length())
			return false;

		if (query.equals(name)) {

			return true;

		}

		int matches = 0;
		for (int i = 0; i < tag_tokens.length; i++) {

			for (int j = 0; j < query_tokens.length; j++) {

				if (tag_tokens[i].equals(query_tokens[j])) {

					matches++;

				}
			}
		}

		if (matches >= tag_tokens.length)
			return true;

		return false;
	}

	public void performPageRank_temp() {

		PageRank<DeliciousNode, DeliciousEdge> ranker = new PageRank<DeliciousNode, DeliciousEdge>(
				graph.graph1, 0.15);

		ranker.acceptDisconnectedGraph(true);
		// ranker.setMaxIterations(1000);
		System.out.println(ranker.getIterations());
		// ranker.setEdgeWeights(edge_weights)
		System.out.println("Running page rank..");
		ranker.evaluate();

		Enumeration<String> nodes = graph.nodes.keys();
		Hashtable<String, Float> scores = new Hashtable<String, Float>();
		while (nodes.hasMoreElements()) {
			String node = nodes.nextElement();

			DeliciousNode no = graph.nodes.get(node);

			if (no.getName().equals("")) {
				continue;
			}
			double s = ranker.getVertexScore(no);
			scores.put(no.getName(), (float) s);

			// System.out.println("The PageRank score of " + no + " is: " + );

		}

		Iterator<Entry<String, Float>> tuples = Sorting.sortHashNumericValues(
				scores, true);
		while (tuples.hasNext()) {
			Entry<String, Float> t = tuples.next();
			System.out.println(t.getKey() + "\t" + t.getValue());
		}

	}

	public static void main(String argvs[]) {

		String prefix = "/Users/sergioduarte/projects/data/graph/";

		String single_query = "/Users/sergioduarte/projects/data/results/single_query/star_wars.txt";

		// String path =
		// "/media/sata__/data/delicious_graph/delicious_graphML_raw_ids_dmoz.txt";
		String path = prefix + "delicious_graphML_raw_ids_dmoz_youth.txt";

		String node_node = path = prefix
				+ "delicious_graphML_node_node_mix_kidsteens_simple.txt";
		// AgeAggregator agg= new AgeAggregator(true, true, true, true, false,
		// false, true);

		// PageRankSuggestions page = new PageRankSuggestions(path,
		// node_node,agg);

		// page.performPageRankWithPriors();

		// page.performPageRank();
	}

	public Hashtable<String, Float> calculateQueryRatios(
			Hashtable<String, Float> q) {

		Hashtable<String, Float> qq = new Hashtable<String, Float>();

		Enumeration<String> keys = q.keys();
		float total = 0.0f;
		while (keys.hasMoreElements()) {
			String element = keys.nextElement();

			if (element.contains("school")) {

				// System.out.println(graph.nodes.containsKey(element) +
				// "---\t "+ element);
			}
			if (graph.nodes.containsKey(element)) {
				total = total + q.get(element);
			}

		}

		keys = q.keys();
		while (keys.hasMoreElements()) {
			String tag = keys.nextElement();
			if (graph.nodes.containsKey(tag)) {
				if (q.get(tag) >= query_threshold_freq) {
					// if(tag.equals("characterized")){System.out.println("aqui si estuvo characterizeds "
					// );}
					qq.put(tag, q.get(tag) / total);
				}
			}

			// System.out.println(tag + "\t"+ qq.get(tag));
		}
		// System.out.println( q+"\t"+ qq);
		return qq;
	}

	@Override
	public ArrayList<Entry<String, Float>> rankQuery(String query,
			Hashtable<String, Float> q, int limit) {

		Hashtable<String, Float> qq = calculateQueryRatios(q);
		// TODO Auto-generated method stub
		System.out.println(query + "\t" + qq);
		return performPageRank(qq, null, query, limit, null);

		// return performPageRanKAbsorving(qq,query,limit);
		// return null;
	}

	@Override
	public ArrayList<Entry<String, Float>> rankQuery(String query, int limit) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public ArrayList<Entry<String, Float>> rankQuery(String query,
			Hashtable<String, Float> q, Hashtable<String, Float> q_topics,
			int limit) {

		Hashtable<String, Float> qq = calculateQueryRatios(q);
		// TODO Auto-generated method stub
		System.out.println(query + "\t" + qq);
		// return performPageRankDiver(qq, q_topics,query,limit,null);

		return performPageRank(qq, q_topics, query, limit, null);

	}

}